package emulator.hardware.io.porthistory;

import java.util.Observable;
import java.util.Observer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import emulator.hardware.bits.SharedBit;
import emulator.hardware.io.Port6522;
import emulator.hardware.io.Port6522Listener;
import emulator.hardware.io.Via6522;
import emulator.history.BitStateChange;
import emulator.history.BitStateHistory;
import emulator.util.events.Event;
import emulator.util.events.EventAggregator;

public class PortStateCollector
{
	static Logger logger = LogManager.getLogger(PortStateCollector.class.getName());
	private EventAggregator eventAggregator;
	private BitStateHistory history;
	private Via6522 via;
	private SharedBit io;
	private long currentTick;
	private static boolean useEventAggregator = false; 

	public PortStateCollector(EventAggregator eventAggregator, BitStateHistory history, Via6522 via, Port6522 port, SharedBit io)
	{
		this.eventAggregator = eventAggregator;
		this.history = history;
		this.via = via;
		this.io = io;
		currentTick = 0;
		
		port.addPortListener(new PortListener());
		Observer ioObserver = (useEventAggregator ? new IoObserverWithAggregator() : new IoObserver());
		ioObserver.update(null, null);
		io.addObserver(ioObserver);
	}

	public class PortListener implements Port6522Listener
	{
		@Override
		public void handlePre(Port6522 port)
		{
			currentTick = via.getTicks();
		}

		@Override
		public void handlePost(Port6522 port)
		{
		}
	}

	public class IoObserverWithAggregator implements Observer
	{
		@Override
		public void update(Observable o, Object arg)
		{
			try {
				eventAggregator.fire(new IoStateEvent(currentTick, io.getValue()));
			} catch (InterruptedException ex) 
			{
				logger.error("Event aggregator was interrupted", ex);
			}
		}
	}

	
	public class IoObserver implements Observer
	{
		@Override
		public void update(Observable o, Object arg)
		{
			history.add(new BitStateChange(currentTick, io.getValue()));
		}
	}

	public class IoStateEvent implements Event
	{
		private long tick;
		private boolean value;

		public IoStateEvent(long tick, boolean value)
		{
			this.tick = tick;
			this.value = value;
		}

		@Override
		public void execute()
		{
			history.add(new BitStateChange(tick, value));
		}
	}
}
